#include "mockif.hpp"
#include <cstdint>
#include <stdio.h>

extern price_t g_hop;
const int commision = 5; // 5 hop = 60

namespace Trade {

void
Deal::print() noexcept
{
  static const char* action_str[] = { "Open", "Close" };
  printf(
    "\033[38;5;46mDeal\033[0m \033[1;31m%d\033[0m %s | deal %.02lf, %d | "
    "avg %.02lf, %d |",
    d,
    action_str[action],
    hpx,
    vol,
    avg_hpx,
    pos_vol);
  if (action == 1) // close
    printf("Rvn %.02lf %.02lf mw %.02lf ml %.02lf",
           rvn_hop,
           revenue,
           rvn_max_win,
           rvn_max_lost);
  printf(" | g %ld\n", idx);
}

void
Revenue::print(const int64_t idx) const noexcept
{
  printf("\033[38;5;46m& RVN &\033[0m|\033[38;5;46mFloat\033[0m L %.02lf S "
         "%.02lf | \033[38;5;46mClosed\033[0m L %.02lf S %.02lf All "
         "%.02lf|\033[38;5;46mTotal\033[0m %.02lf | g%ld\n",
         float_up,
         float_dn,
         close_up,
         close_dn,
         close_rvn,
         all,
         idx);
}

int
MockIF::mock_deal(const int64_t idx,
                  const price_t hpx,
                  const price_t ftime,
                  int v[4]) noexcept
{
  v[0] = mock_try_open(1, hpx, ftime, idx);
  v[1] = mock_try_open(-1, hpx, ftime, idx);
  v[2] = mock_try_close(1, hpx, ftime, idx);
  v[3] = mock_try_close(-1, hpx, ftime, idx);
  int all = (v[0] + v[1] + v[2] + v[3]);
  mock_update_float_revenue(hpx);

  if (v[0] || v[2]) {
    pos_up.print(hpx, idx);
  }
  if (v[1] || v[3]) {
    pos_dn.print(hpx, idx);
  }

  return all;
}

int
MockIF::mock_try_open(const dir_t d,
                      const price_t hpx,
                      const time_t tm,
                      const int64_t idx) noexcept
{
  auto open = [&](const OrderPtr& o) {
    auto pos = (d > 0) ? &pos_up : &pos_dn;
    pos->d = d;
    pos->avg_hpx =
      (pos->avg_hpx * pos->vol + hpx * o->vol) / (pos->vol + o->vol);
    pos->vol += o->vol;
    pos->float_hop_rvn = (hpx - pos->avg_hpx) * d;
    pos->float_revenue = pos->float_hop_rvn * pos->vol * g_hop;
    pos->max_hop_win = pos->max_hop_lost = pos->float_hop_rvn;

    DealPtr deal = std::make_shared<Deal>();
    deal->action = 0;
    deal->d = d;
    deal->tm = tm;
    deal->idx = idx;
    deal->hpx = hpx;
    deal->avg_hpx = pos->avg_hpx;
    deal->vol = o->vol;
    deal->pos_vol = pos->vol;
    dv.push_back(deal);

    if (d > 0) {
      revenue.float_up = pos->float_revenue;
    } else {
      revenue.float_dn = pos->float_revenue;
    }
    revenue.float_rvn = revenue.float_dn + revenue.float_up;
    revenue.all = revenue.float_rvn + revenue.close_rvn;

    if (on_open)
      on_open(o, deal);
  };

  auto ol = (d > 0) ? &oo_up : &oo_dn;
  int all_vol = 0;
  for (auto it = ol->begin(); it != ol->end();) {
    if (d * ((*it)->hpx - hpx) >= 0) {
      open(*it);
      all_vol += (*it)->vol;
      it = ol->erase(it);
    } else {
      ++it;
    }
  }

  return all_vol;
}

int
MockIF::mock_try_close(const dir_t d,
                       const price_t hpx,
                       const time_t tm,
                       const int64_t idx) noexcept
{
  auto close = [&](const OrderPtr& o) {
    auto pos = (d > 0) ? &pos_up : &pos_dn;
    if (pos->vol < o->vol)
      return false;
    pos->d = d;
    price_t float_hop_rvn = (hpx - pos->avg_hpx) * d;

    DealPtr deal = std::make_shared<Deal>();
    deal->action = 1;
    deal->d = d;
    deal->tm = tm;
    deal->idx = idx;
    deal->hpx = hpx;
    deal->avg_hpx = pos->avg_hpx;
    deal->vol = o->vol;
    deal->pos_vol = pos->vol;
    deal->rvn_hop = float_hop_rvn - commision;
    deal->revenue = deal->rvn_hop * deal->vol * g_hop;
    deal->rvn_max_win = pos->max_hop_win;
    deal->rvn_max_lost = pos->max_hop_lost;
    dv.push_back(deal);

    pos->vol -= o->vol;
    if (pos->vol > 0) {
      pos->float_hop_rvn = float_hop_rvn;
      pos->float_revenue = float_hop_rvn * g_hop * pos->vol;
      pos->max_hop_win = pos->max_hop_lost = pos->float_hop_rvn;
    } else {
      pos->reset();
    }

    if (d > 0) {
      revenue.float_up = pos->float_revenue;
      revenue.close_up += deal->revenue;
    } else {
      revenue.float_dn = pos->float_revenue;
      revenue.close_dn += deal->revenue;
    }
    revenue.float_rvn = revenue.float_dn + revenue.float_up;
    revenue.close_rvn = revenue.close_up + revenue.close_dn;
    revenue.all = revenue.float_rvn + revenue.close_rvn;

    if (on_close)
      on_close(o, deal);

    return true;
  };

  auto cl = (d > 0) ? &co_up : &co_dn;
  int all_vol = 0;
  for (auto it = cl->begin(); it != cl->end();) {
    if (d * (hpx - (*it)->hpx) >= 0) {
      if (close(*it)) {
        all_vol += (*it)->vol;
        it = cl->erase(it);
        continue;
      }
    }
    ++it;
  }
  return all_vol;
}

int
MockIF::mock_place_open_order(const dir_t d,
                              const price_t hpx,
                              const price_t hp_now,
                              const int vol,
                              const time_t tm,
                              const int64_t idx) noexcept
{
  auto ol = (d > 0) ? &oo_up : &oo_dn;
  OrderPtr o = std::make_shared<Order>();
  o->d = d;
  o->vol = vol;
  o->tm = tm;
  o->hpx = hpx;
  o->idx = idx;
  ol->push_back(o);
  printf("\033[38;5;201m!-ORDER-! %d\033[0m, %.02lf(%.02lf) Vol %d, g%ld -- "
         "size %lu\n",
         d,
         hpx,
         hp_now - hpx,
         vol,
         idx,
         ol->size());

  return (0);
}

int
MockIF::mock_place_close_order(const dir_t d,
                               const price_t hpx,
                               const price_t hp_now,
                               const int vol,
                               const time_t tm,
                               const int64_t idx) noexcept
{
  auto cl = (d > 0) ? &co_up : &co_dn;
  OrderPtr o = std::make_shared<Order>();
  o->d = d;
  o->vol = vol;
  o->hpx = hpx;
  o->tm = tm;
  o->idx = idx;
  cl->push_back(o);
  printf("\033[38;5;201m!-CLOSE-! %d\033[0m, %.02lf(%.02lf), Vol %d g%ld -- "
         "size %lu\n",
         d,
         hpx,
         hp_now - hpx,
         vol,
         idx,
         cl->size());
  return (0);
}

int
MockIF::mock_query_open_order(const dir_t d, const int64_t idx) noexcept
{
  auto ol = (d > 0) ? &oo_up : &oo_dn;
  return int(ol->size());
}

int
MockIF::mock_query_close_order(const dir_t d, const int64_t idx) noexcept
{
  auto co = (d > 0) ? &co_up : &co_dn;
  return int(co->size());
}

int
MockIF::mock_recall_all_order(const int64_t idx) noexcept
{
  mock_recall_all_close_order(1, idx);
  mock_recall_all_close_order(-1, idx);
  mock_recall_all_open_order(1, idx);
  mock_recall_all_open_order(-1, idx);
  return (0);
}

int
MockIF::mock_recall_all_open_order(const dir_t d, const int64_t idx) noexcept
{
  auto ol = (d > 0) ? &oo_up : &oo_dn;
  if (!ol->empty()) {
    printf("\033[38;5;201m!-!-! Cancle Open %d\033[0m, %lu order(s) g%ld\n",
           d,
           ol->size(),
           idx);
    ol->clear();
  }
  return (0);
}

int
MockIF::mock_recall_all_close_order(const dir_t d, const int64_t idx) noexcept
{
  auto co = (d > 0) ? &co_up : &co_dn;
  if (!co->empty()) {
    printf(
      "\033[38;5;201m!-!-! Cancle Close %d\033[0m, %lu order(s) All g%ld\n",
      d,
      co->size(),
      idx);
    co->clear();
  }
  return (0);
}

void
MockIF::mock_update_float_revenue(const price_t hpx) noexcept
{
  pos_up.update_float_revenue(hpx);
  pos_dn.update_float_revenue(hpx);
  revenue.float_up = pos_up.float_revenue;
  revenue.float_dn = pos_dn.float_revenue;
  revenue.float_rvn = revenue.float_up + revenue.float_dn;
  revenue.all = revenue.float_rvn + revenue.close_rvn;

  auto set_target_lost_a_lot = [&](const dir_t d) {

  };

  auto cancel_target_lost_a_lot = [&](const dir_t d) {

  };

  if (pos_up.float_hop_rvn < -20) {
    set_target_lost_a_lot(1);
  } else if (pos_up.float_hop_rvn > 5) {
    cancel_target_lost_a_lot(1);
  }

  if (pos_dn.float_hop_rvn < -20) {
    set_target_lost_a_lot(-1);
  } else if (pos_dn.float_hop_rvn > 6) {
    cancel_target_lost_a_lot(-1);
  }
}

bool
MockIF::mock_query_position(const dir_t d, int* vol, price_t* avg_hpx) noexcept
{
  auto pos = (d > 0) ? &pos_up : &pos_dn;
  *vol = pos->vol;
  *avg_hpx = pos->avg_hpx;
  return (pos->vol != 0);
}

int
MockIF::mock_query_position_volume(const dir_t d) noexcept
{
  return ((d > 0) ? pos_up : pos_dn).vol;
}

bool
MockIF::mock_query_revenue(const dir_t d,
                           price_t& float_revenue,
                           price_t& close_revenue) noexcept
{
  float_revenue = ((d > 0) ? pos_up : pos_dn).float_revenue;
  close_revenue = (d > 0) ? revenue.close_up : revenue.close_dn;
  return (float_revenue > 0 || close_revenue > 0);
}

void
MockIF::mock_print_orders_() const noexcept
{
  auto print_orders = [](const OrderList* const ol) {
    for (auto it = ol->begin(); it != ol->end(); ++it) {
      printf("    %d, %.02lf, V%d, g%ld\n",
             (*it)->d,
             (*it)->hpx,
             (*it)->vol,
             (*it)->idx);
    }
  };

  printf(" -- Open +1 Orders -- size %lu:\n", oo_up.size());
  print_orders(&oo_up);
  printf(" -- Open -1 Orders -- size %lu:\n", oo_dn.size());
  print_orders(&oo_dn);
  printf(" -- Close +1 Orders -- size %lu:\n", co_up.size());
  print_orders(&co_up);
  printf(" -- Close -1 Orders -- size %lu:\n", co_dn.size());
  print_orders(&co_dn);
}

void
MockIF::mock_show_deal_record() noexcept
{
  for (const auto& deal : dv) {
    deal->print();
  }
}

void
MockIF::mock_close_position(const dir_t d,
                            const price_t hpx,
                            const price_t hp_now,
                            const time_t tm,
                            const int64_t idx) noexcept
{
  int vol;
  price_t avg_hpx;
  auto ok = mock_query_position(d, &vol, &avg_hpx);
  if (ok) {
    mock_place_close_order(d, hpx, hp_now, vol, tm, idx);
  }
}

void
MockIF::mock_close_all_position(const price_t hpx,
                                const price_t hp_now,
                                const time_t tm,
                                const int64_t idx) noexcept
{
  mock_close_position(1, hpx, hp_now, tm, idx);
  mock_close_position(-1, hpx, hp_now, tm, idx);
}

bool
MockIF::is_good_close_price(const dir_t d, const price_t hpx) noexcept
{
  int vol;
  price_t avg_hpx;
  auto ok = mock_query_position(d, &vol, &avg_hpx);
  if (ok) {
    return ((hpx - avg_hpx) * d > commision);
  }
  return true;
}
} // namespace Trade